package org.finesys.chat.core.base.document;

import org.finesys.common.core.util.ValidationUtil;

import java.util.*;

public class Metadata {

    private static final Set<Class<?>> SUPPORTED_VALUE_TYPES = new LinkedHashSet<>();
    private final Map<String, Object> metadata;

    public Metadata() {
        this.metadata = new HashMap<>();
    }

    public Metadata(Map<String, Object> metadata) {
        ValidationUtil.ensureNotNull(metadata, "metadata").forEach((key, value) -> {
            validate(key, value);
            if (SUPPORTED_VALUE_TYPES.contains(value.getClass())) {
                throw new IllegalArgumentException(String.format(key, value, value.getClass().getName(), SUPPORTED_VALUE_TYPES));
            }
        });
        this.metadata = metadata;
    }

    private static void validate(String key, Object value) {
        ValidationUtil.ensureNotNull(key, "metadata key");
        ValidationUtil.ensureNotNull(value, "metadata value type");
    }

    public Object get(String key) {
        return metadata.get(key);
    }

    public boolean containsKey(String key) {
        return metadata.containsKey(key);
    }

    public Metadata put(String key, Object value) {
        validate(key, value);
        metadata.put(key, value);
        return this;
    }

    public Metadata remove(String key) {
        metadata.remove(key);
        return this;
    }

    public Map<String, String> asMap() {
        Map<String, String> map = new HashMap<>();
        metadata.forEach((k, v) -> {
            map.put(k, String.valueOf(v));
        });
        return map;
    }

    public Metadata copy() {
        return new Metadata(this.metadata);
    }

    public Map<String, Object> toMap() {
        return new HashMap<>(metadata);
    }

    public static Metadata from(Map<String, Object> metadata) {
        return new Metadata(metadata);
    }

    public static Metadata from(String key, Object value) {
        return new Metadata().put(key, value);
    }

    public static Metadata metadata(String key, Object value) {
        return from(key, value);
    }

    static {
        SUPPORTED_VALUE_TYPES.add(String.class);
        SUPPORTED_VALUE_TYPES.add(UUID.class);
        SUPPORTED_VALUE_TYPES.add(Integer.TYPE);
        SUPPORTED_VALUE_TYPES.add(Integer.class);
        SUPPORTED_VALUE_TYPES.add(Long.TYPE);
        SUPPORTED_VALUE_TYPES.add(Long.class);
        SUPPORTED_VALUE_TYPES.add(Float.TYPE);
        SUPPORTED_VALUE_TYPES.add(Float.class);
        SUPPORTED_VALUE_TYPES.add(Double.TYPE);
        SUPPORTED_VALUE_TYPES.add(Double.class);
    }
}
